﻿#requires –version 2.0

Function Install-Patches {

#region Help

<#
.SYNOPSIS
	Install approved Windows patches from a WSUS server and check/upgrade Vmware 
	Tools/Hardware if needed.
.DESCRIPTION
	This Powershell script can be used to install Windows patches distributed
	from a WSUS server. Plus upgrade Vmware Vmtools and Vmware Hardware version
	on the guest if needed.
	
	BASIC SCRIPT FLOW:
	--------------------------------------------------------------------
	1)	Test Connection
	2)	Check Disk Space
	3)	Check for Pending Reboot (Reboot if Pending)
	4)	Download, Install and Reboot for Windows Patches until none left
	5)	Check, Upgrade and Reboot if VmTools update needed
	6)	Check and Upgrade Vmware hardware version if needed
	7)	Results Report and Write Logs
.NOTES
	VERSION:    2.6.4
	AUTHOR:     Levon Becker
	EMAIL:      PowerShell.Guru@BonusBits.com 
	ENV:        Powershell v2.0, CLR 4.0+, PowerCLI 4.1+
	TOOLS:      PowerGUI Script Editor
	
	REQUIREMENTS
	===================================================================
	
	System Running the Script
	-------------------------
	1) Powershell v2.0+
	2) .Net 4.0+
	3) PowerShell running CLR 4.0+
		a) http://wiki.bonusbits.com/main/HowTo:Enable_.NET_4_Runtime_for_PowerShell_and_Other_Applications
	4) Execution Policy Unrestricted or RemoteSigned
	5) Remote registry, WIMRM and RPC services running
	2) Firewall access to remote computer
	3) Local Admin Permissions on remote computer
	4) Short name resolution for remote computer
	5) Use the actual hostname and not a DNS alias or IP
	6) If using Vmtools upgrade option 
		a) PowerCLI v4.1 or Higher
		b) Permissions to vCenter or Standalone host
		c) VM name matching the hostname for remote computer
	
	Remote Computer to be Patched
	------------------------------
	1) Setup to pull patches from WSUS server
	2) Client in a WSUS ComputerName Group with approved patches
	3) Currently Powershell is required on the remote computer
	4} Remote registry, WIMRM and RPC services running
	5) Firewall set to allow all TCP from scipt host IP (easiest)
	
	TESTED OPERATING SYSTEMS
	------------------------
	Windows Server 
		2000
		2003
		2008
		2008 R2
	Windows Workstation
		XP
		Vista
		7
.INPUTS
	ComputerName    Single Hostname
	List            List of Hostnames
	FileName        File with List of Hostnames
	FileBrowser     File with List of Hostnames
	
	DEFAULT FILENAME PATH
	---------------------
	HOSTLISTS
	%USERPROFILE%\Documents\HostList
.OUTPUTS
	DEFAULT PATHS
	-------------
	RESULTS
	%USERPROFILE%\Documents\Results\Install-Patches
	
	LOGS
	%USERPROFILE%\Documents\Logs\Install-Patches
	+---History
	+---JobData
	+---Latest
	+---Temp
	+---WIP
.EXAMPLE
	Install-Patches -ComputerName server01 
	Patch a single computer.
.EXAMPLE
	Install-Patches server01 
	Patch a single computer.
	The ComputerName parameter is in position 0 so it can be left off for a
	single computer.
.EXAMPLE
	Install-Patches -List server01,server02
	Patch a list of hostnames comma separated without spaces.
.EXAMPLE
	Install-Patches -List $MyHostList 
	Patch a list of hostnames from an already created array variable.
	i.e. $MyHostList = @("server01","server02","server03")
.EXAMPLE
	Install-Patches -FileName HostList.txt
	Patch a list of hosts from a file named HostList.txt that is located in the
	HostList directory.
	The host names need to be listed one per line or comma separated.
	Do not use FQDN
.EXAMPLE
	Install-Patches -FileName ServerList.csv -MaxJobs 200 -SkipDiskSpaceCheck -SkipVmTools -SkipVmHardware
	FileName:
		Patch a list of hosts from a file named ServerList.csv that is located 
		in the HostList directory.
		The host names need to be listed one per line or comma separated.
		Do not use FQDN
	MaxJobs:
		This parameter sets the maximum allowed background jobs for the 
		patching task.
	SkipDiskSpaceCheck:
		This switch will skip checking the hard drive free space
	SkipVmTools:
		This switch will skip checking and upgrading Vmware VmTools.
	SkipVmHardware:
		This switch will skip the task to check/update the Vmware VM Hardware 
		version.	
.EXAMPLE
	Install-Patches -FileBrowser 
	This switch will launch a separate file browser window.
	In the window you can browse and select a text or csv file from anywhere
	accessible by the local computer that has a list of host names.
	The host names need to be listed one per line or comma separated.
	This list of system names will be used to perform the script tasks for 
	each host in the list.
.EXAMPLE
	Install-Patches -FileBrowser -SkipAllVmware
	FileBrowser:
		This switch will launch a separate file browser window.
		In the window you can browse and select a text or csv file from anywhere
		accessible by the local computer that has a list of host names.
		The host names need to be listed one per line or comma separated.
		This list of system names will be used to perform the script tasks for 
		each host in the list.
	SkipAllVmware:
		This switch will skip all Vmware tasks and requirements.
.EXAMPLE
	Install-Patches -FileBrowser -SkipOutGrid -SkipVmHardware
	FileBrowser:
		This switch will launch a separate file browser window.
		In the window you can browse and select a text or csv file from anywhere
		accessible by the local computer that has a list of host names.
		The host names need to be listed one per line or comma separated.
		This list of system names will be used to perform the script tasks for 
		each host in the list.
	SkipOutGrid:
		This switch will skip the results poppup windows at the end.
	SkipVmHardware:
		This switch will skip the task to check/update the Vmware VM Hardware 
		version.	
.PARAMETER ComputerName
	Short name of Windows host to patch
	Do not use FQDN 
.PARAMETER List
	A PowerShell array List of servers to patch or comma separated list of host
	names to perform the script tasks on.
	-List server01,server02
	@("server1", "server2") will work as well
	Do not use FQDN
.PARAMETER FileName
	This parameter excepts a file name of a Text or Comma Delimited list of host 
	names in the HostList directory.
	The host names need to be listed one per line or comma separated.
	Do not use FQDN
.PARAMETER FileBrowser
	This switch will launch a separate file browser window.
	In the window you can browse and select a text or csv file from anywhere
	accessible by the local computer that has a list of host names.
	The host names need to be listed one per line or comma separated.
	This list of system names will be used to perform the script tasks for 
	each host in the list.
.PARAMETER MaxJobs
	Maximum amount of background jobs to run simultaneously. 
	Adjust depending on how much memory and load the localhost can handle.
	It's not recommended to set higher than 500.
	Default = 400
.PARAMETER JobQueTimeout
	Maximum amount of time in seconds to wait for the background jobs to finish 
	before timing out. 	Adjust this depending out the speed of your environment 
	and based on the maximum jobs ran simultaneously.
	
	If the MaxJobs setting is turned down, but there are a lot of servers this 
	may need to be increased.
	
	This timer starts after all jobs have been queued.
	Default = 10800 (3 hours)
.PARAMETER MinFreeMB
	This is the value used when checking C: hard drive space.  
	The default	is 500MB. 
	Enter a number in Megabytes.
.PARAMETER vCenter
	Vmware vSphere Virtual Center FQDN.
	It is used for several tasks including VmTools check/upgrade, VmHardware
	check/upgrade and system information queries.
.PARAMETER UseAltViCreds
	This switch will trigger a be prompt to enter alternate credentials for 
	connecting to vCenter.
.PARAMETER UseAltPCCreds
	This switch will trigger a be prompt to enter alternate credentials for 
	connecting to all the computers. (WIP)
.PARAMETER SkipOutGrid
	This switch will skip displaying the end results that uses Out-GridView.
.PARAMETER SkipAllVmware
	This switch will skip all functions that require PowerCLI.
.PARAMETER SkipDiskSpaceCheck
	If this switch is present the task to verify if there is enough Disk Space 
	on each remote computer will be skipped.
.PARAMETER SkipVMHardware
	This switch will skip the Check and Upgrade of Vmware Hardware task.
.PARAMETER SkipVMTools
	This switch will skip the Check and Upgrade Vmware Tools task.
.LINK
	http://wiki.bonusbits.com/main/PSScript:Install-Patches
	http://wiki.bonusbits.com/main/PSModule:WindowsPatching
	http://wiki.bonusbits.com/main/HowTo:Use_WindowsPatching_PowerShell_Module_to_Automate_Patching_with_WSUS_as_the_Client_Patch_Source
	http://wiki.bonusbits.com/main/HowTo:Enable_.NET_4_Runtime_for_PowerShell_and_Other_Applications
	http://wiki.bonusbits.com/main/HowTo:Setup_PowerShell_Module
	http://wiki.bonusbits.com/main/HowTo:Enable_Remote_Signed_PowerShell_Scripts
#>

#endregion Help

#region Parameters

	[CmdletBinding()]
	    Param (
	        [parameter(Mandatory=$false,Position=0)][string]$ComputerName,
	        [parameter(Mandatory=$false)][string]$FileName,
			[parameter(Mandatory=$false)][array]$List,
			[parameter(Mandatory=$false)][string]$vCenter,
			[parameter(Mandatory=$false)][int]$MaxJobs = '400', #Adjust depending on how much load the localhost can handle
			[parameter(Mandatory=$false)][int]$JobQueTimeout = '10800', #This timer starts after all jobs have been queued.
			[parameter(Mandatory=$false)][int]$MinFreeMB = '500',
			[parameter(Mandatory=$false)][switch]$SkipDiskSpaceCheck,
			[parameter(Mandatory=$false)][switch]$SkipVMHardware,
			[parameter(Mandatory=$false)][switch]$SkipVMTools,
			[parameter(Mandatory=$false)][switch]$SkipOutGrid,
			[parameter(Mandatory=$false)][switch]$FileBrowser,
			[parameter(Mandatory=$false)][switch]$UseAltPCCreds,
			[parameter(Mandatory=$false)][switch]$UseAltViCreds,
			[parameter(Mandatory=$false)][switch]$SkipAllVmware
	       )
	   
#endregion Parameters

	If (!$Global:WindowsPatchingDefaults) {
		. "$Global:WindowsPatchingModulePath\SubScripts\MultiFunc_Show-WPMErrors_1.0.0.ps1"
		Show-WPMDefaultsMissingError
	}

	# GET STARTING GLOBAL VARIABLE LIST
	New-Variable -Name StartupVariables -Force -Value (Get-Variable -Scope Global | Select -ExpandProperty Name)
	
	# CAPTURE CURRENT TITLE
	[string]$StartingWindowTitle = $Host.UI.RawUI.WindowTitle

	# SET VCENTER HOSTNAME IF NOT GIVEN AS PARAMETER FROM GLOBAL DEFAULT
	If (!$vCenter) {
		If ($Global:WindowsPatchingDefaults) {
			$vCenter = ($Global:WindowsPatchingDefaults.vCenter)
		}
	}
	
	[boolean]$filebrowserused = $false
	[string]$HostListPath = ($Global:WindowsPatchingDefaults.HostListPath)
	
#region Prompt: Missing Input

	#region Prompt: FileBrowser
	
		If ($FileBrowser.IsPresent -eq $true) {
			. "$Global:WindowsPatchingModulePath\SubScripts\Func_Get-FileName_1.0.0.ps1"
			Clear
			Write-Host 'SELECT FILE CONTAINING A LIST OF HOSTS TO PATCH.'
			Get-FileName -InitialDirectory $HostListPath -Filter "Text files (*.txt)|*.txt|Comma Delimited files (*.csv)|*.csv|All files (*.*)|*.*"
			[string]$FileName = $Global:GetFileName.FileName
			[string]$HostListFullName = $Global:GetFileName.FullName
			[boolean]$filebrowserused = $true
		}
		Else {
			[boolean]$filebrowserused = $false
		}
	
	#endregion Prompt: FileBrowser
	
	#region Prompt: Host Input

		If (!($FileName) -and !($ComputerName) -and !($List)) {
			# Set to to trigger other prompts, guessing user doesn't want to type out parameters.
			[boolean]$hostinputprompt = $true
			Clear
			$promptitle = ''
			
			$message = "SELECT HOST INPUT METHOD:"
			
			# HM = Host Method
			$hmc = New-Object System.Management.Automation.Host.ChoiceDescription "&ComputerName", `
			    'Enter a single hostname'

			$hmf = New-Object System.Management.Automation.Host.ChoiceDescription "&FileName", `
			    'Text file name that contains a List of ComputerNames'
			
			$hml = New-Object System.Management.Automation.Host.ChoiceDescription "&List", `
			    'Enter a List of hostnames separated by a commna without spaces'
			
			$exit = New-Object System.Management.Automation.Host.ChoiceDescription "E&xit", `
			    'Exit Script'

			$options = [System.Management.Automation.Host.ChoiceDescription[]]($hmc, $hmf, $hml, $exit)
			
			$result = $host.ui.PromptForChoice($promptitle, $message, $options, 3) 
			
			# RESET WINDOW TITLE AND BREAK IF EXIT SELECTED
			If ($result -eq 3) {
				Clear
				Write-Host ''
				Break
			}
			Else {
			Switch ($result)
				{
				    0 {$hmoption = 'ComputerName'} 
				    1 {$hmoption = 'FileName'}
					2 {$hmoption = 'List'}
				}
			}
			
			# PROMPT FOR ComputerName NAME
			If ($hmoption -eq 'ComputerName') {
				Do {
					Do {
						Clear
						Write-Host ''
	#					Write-Host 'Short name of a single host.'
						$ComputerName = $(Read-Host -Prompt 'ENTER COMPUTERNAME')
					}
					Until ($ComputerName)
					
					If ((Test-Connection -ComputerName $ComputerName -Count 2 -Quiet) -eq $true) {
						[boolean]$pinggood = $true
					}
					Else {
						[boolean]$pinggood = $false
						Write-Host ''
						Write-Host "ERROR: Ping Failed to ($ComputerName)" -ForegroundColor White -BackgroundColor Red
						Write-Host ''
						$title = ''
						$message = 'CONTINUE WITH NON PINGABLE HOST?'

						$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
						    'Continue with patching even though host is not pingable.'

						$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
						    'Stop the script.'

						$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

						$result = $host.ui.PromptForChoice($title, $message, $options, 1) 

						switch ($result)
						{
						    0 {[boolean]$keepgoing = $true} 
						    1 {[boolean]$keepgoing = $false} 
						}
						If ($keepgoing -eq $true) {
							[boolean]$pinggood = $true
						}
					}
				}
				Until ($pinggood -eq $true)
			}
			# PROMPT FOR FileName
			Elseif ($hmoption -eq 'FileName') {
				. "$Global:WindowsPatchingModulePath\SubScripts\Func_Get-FileName_1.0.0.ps1"
				Clear
				Write-Host ''
				Write-Host 'SELECT FILE CONTAINING A LIST OF HOSTS TO PATCH.'
	#			$FileName = $(Read-Host -Prompt 'Enter File Name')
				Get-FileName -InitialDirectory $HostListPath -Filter "Text files (*.txt)|*.txt|Comma Delimited files (*.csv)|*.csv|All files (*.*)|*.*"
				[string]$FileName = $Global:GetFileName.FileName
				[string]$HostListFullName = $Global:GetFileName.FullName
				[boolean]$filebrowserused = $true
			}
			# PROMPT FOR List 
			Elseif ($hmoption -eq 'List') {
				Do {
					Clear
					Write-Host ''
	#				Write-Host 'Enter a List of hostnames separated by a comma without spaces to patch.'
					$commaList = $(Read-Host -Prompt 'Enter List')
					# Read-Host only returns String values, so need to split up the hostnames and put into array
					[array]$List = $commaList.Split(',')
					}
				Until ($List)
			}
			Else {
				Clear
				Write-Host ''
				Write-Host 'ERROR: Host method entry issue' -ForegroundColor White -BackgroundColor Red
				Break
			}
		}
		
		#endregion Prompt: Host Input
		
	#region Prompt: Alternate PC Credentials

			If ($hostinputprompt -eq $true) {
				Clear
				$title = ''
				$message = 'ENTER ALTERNATE PC CREDENTIALS?'
			
				$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
				    'Enter UserName and password for vCenter access instead of using current credintials.'
			
				$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
				    'Do not enter UserName and password for vCenter access. Just use current.'
			
				$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
			
				$result = $host.ui.PromptForChoice($title, $message, $options, 1) 
			
				switch ($result)
				{
				    0 {[switch]$UseAltPCCreds = $true} 
				    1 {[switch]$UseAltPCCreds = $false} 
				}
				If ($UseAltPCCreds.IsPresent -eq $true) {
					Do {
						Try {
							$PCCreds = Get-Credential -ErrorAction Stop
							[boolean]$UseAltPCCredsBool = $true
							[boolean]$getcredssuccess = $true
						}
						Catch {
							[boolean]$getcredssuccess = $false
						}
					}
					Until ($getcredssuccess -eq $true)
				}
				ElseIf ($UseAltPCCreds.IsPresent -eq $false) {
					[boolean]$UseAltPCCredsBool = $false
				}
			}

		#endregion Prompt: Alternate PC Credentials
		
	#region Prompt: Hard Disk Space Check
		
			If ($hostinputprompt -eq $true) {
				Clear
				$title = ''
				$message = 'CHECK C: DRIVE FOR MINIMUM SPACE?'

				$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
				    'Check that there is a minimum of $MinFreeMB MB free space on C:'

				$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
				    'Do not check for minimum disk space on C:'
				
				$exit = New-Object System.Management.Automation.Host.ChoiceDescription "E&xit", `
				    'Exit Script'

				$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no, $exit)

				$result = $host.ui.PromptForChoice($title, $message, $options, 0) 

				# RESET WINDOW TITLE AND BREAK IF EXIT SELECTED
				If ($result -eq 2) {
					Clear
					Write-Host ''
					Break
				}

				switch ($result)
				{
				    0 {[switch]$SkipDiskSpaceCheck = $false} 
				    1 {[switch]$SkipDiskSpaceCheck = $true} 
				}
			}

		#endregion Prompt: Hard Disk Space Check
		
	#region Prompt: Vmware
	
		#region Prompt: Skip All Vmware Tasks
			
			If (($SkipAllVmware.IsPresent -eq $false) -and ($hostinputprompt -eq $true)) {
				Clear
				$title = ''
				$message = 'SKIP ALL VMWARE TASKS?'

				$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
				    'Do not use PowerCLI and vCenter for any tasks.'

				$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
				    'Use PowerCLI and vCenter for specific takss.'
						
				$exit = New-Object System.Management.Automation.Host.ChoiceDescription "E&xit", `
					'Exit Script'

				$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no, $exit)

				$result = $host.ui.PromptForChoice($title, $message, $options, 1) 

				# RESET WINDOW TITLE AND BREAK IF EXIT SELECTED
				If ($result -eq 2) {
					Clear
					Break
				}

				switch ($result)
				{
				    0 {[switch]$SkipAllVmware = $true} 
				    1 {[switch]$SkipAllVmware = $false} 
				}
			}

		#endregion Prompt: Skip All Vmware Tasks

		If (($SkipAllVmware.IsPresent -eq $false) -and ($hostinputprompt -eq $true)) {
		
			#region Prompt: VMGuest Hardware Upgrade
				
				Clear
				$title = ''
				$message = 'CHECK AND UPGRADE VMTOOLS PLUS VMHARDWARE IF NEEDED?'

				$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
				    'Upgrades VM Guest hardware version to v7 if needed on all guests Listed that are virtual machines attached to a 4.x VM Host.'

				$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
				    'Does not check or upgrade VM Guest hardware version.'
						
				$exit = New-Object System.Management.Automation.Host.ChoiceDescription "E&xit", `
					'Exit Script'

				$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no, $exit)

				$result = $host.ui.PromptForChoice($title, $message, $options, 0) 

				# RESET WINDOW TITLE AND BREAK IF EXIT SELECTED
				If ($result -eq 2) {
					Clear
					Break
				}

				switch ($result)
				{
				    0 {[switch]$SkipVMHardware = $false} 
				    1 {[switch]$SkipVMHardware = $true} 
				}

			#endregion Prompt: VMGuest Hardware Upgrade

			#region Prompt: Vmware Vmtools Upgrade

				# Prompt for choice to upgrade Vmtools if Vmhardware prompt answer was No (Else Tools will be checked with VMHU)
				If ($SkipVMHardware.IsPresent -eq $true) {
					Clear
					$title = ''
					$message = 'CHECK AND UPGRADE VMTOOLS IF NEEDED, BUT NOT VMHARDWARE?'

					$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
					    'Upgrades Vmtools if needed on all guests Listed that are virtual machines.'

					$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
					    'Does not upgrade Vmtools on guests.'
						
					$exit = New-Object System.Management.Automation.Host.ChoiceDescription "E&xit", `
					    'Exit Script'	

					$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no, $exit)

					$result = $host.ui.PromptForChoice($title, $message, $options, 0) 
					
					# RESET WINDOW TITLE AND BREAK IF EXIT SELECTED
					If ($result -eq 2) {
						Clear
						Break
					}
					
					switch ($result)
					{
					    0 {[switch]$SkipVMTools = $false} 
					    1 {[switch]$SkipVMTools = $true} 
					}
				}

			#endregion Prompt: Vmware Vmtools Upgrade

			#region Prompt: vCenter

				If (($SkipVMTools.IsPresent -eq $false) -or ($SkipVMHardware.IsPresent -eq $false)) {
					If (($vCenter -eq '') -or ($vCenter -eq $null)) {
						Do {
							Clear
							$vCenter = $(Read-Host -Prompt 'ENTER vCENTER or ESX HOSTNAME')
							
							If ((Test-Connection -ComputerName $vCenter -Count 2 -Quiet) -eq $true) {
								[boolean]$pinggood = $true
							}
							Else {
								[boolean]$pinggood = $false
								Write-Host ''
								Write-Host "ERROR: Ping Failed to ($vCenter)" -ForegroundColor White -BackgroundColor Red
								Write-Host ''
								$title = ''
								$message = 'CONTINUE WITH NON PINGABLE HOST?'

								$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
								    'Continue with patching even though host is not pingable.'

								$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
								    'Stop the script.'

								$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

								$result = $host.ui.PromptForChoice($title, $message, $options, 1) 

								switch ($result)
								{
								    0 {[boolean]$keepgoing = $true} 
								    1 {[boolean]$keepgoing = $false} 
								}
								If ($keepgoing -eq $true) {
									[boolean]$pinggood = $true
								}
							}
						}
						Until ($pinggood -eq $true)
					} #IF vCenter doesn't have a value
				} #IF Skip VmTools or VmHardware not present

			#endregion Prompt: vCenter

			#region Prompt: Alternate VIM Credentials

				# If GetVmTools or GetVmHardware is YES, then ask if want to enter Alternate Credentials for vCenter
				If (($SkipVMTools.IsPresent -eq $false) -or ($SkipVMHardware.IsPresent -eq $false)) {
					Clear
					$title = ''
					$message = 'ENTER ALTERNATE VIM CREDENTIALS?'
				
					$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
					    'Enter UserName and password for vCenter access instead of using current credintials.'
				
					$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
					    'Do not enter UserName and password for vCenter access. Just use current.'
				
					$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
				
					$result = $host.ui.PromptForChoice($title, $message, $options, 1) 
				
					switch ($result)
					{
					    0 {[switch]$UseAltViCreds = $true} 
					    1 {[switch]$UseAltViCreds = $false} 
					}
				}
				
			#endregion Prompt: Alternate VIM Credentials

		}

	#endregion Prompt: Vmware

#endregion Prompt: Missing Input

#region Prompt: Get Alt VI Credentials

		# Prompt for ViCreds if needed
		If ($UseAltViCreds.IsPresent -eq $true) {
			Do {
				Try {
					$ViCreds = Get-Credential -ErrorAction Stop
					[boolean]$getcredssuccess = $true
				}
				Catch {
					[boolean]$getcredssuccess = $false
				}
			}
			Until ($getcredssuccess -eq $true)
		}
		# If -AltViCreds switch is present then prompt for Alternate Credentials for vCenter
		ElseIf ($UseAltViCreds.IsPresent -eq $true) {
				Write-Host ''
				$ViCreds = Get-Credential
		}

#endregion Prompt: Get Alt VI Credentials

#region Variables

	# DEBUG
	$ErrorActionPreference = "Inquire"
	
	# SET ERROR MAX LIMIT
	$MaximumErrorCount = '1000'

	# SCRIPT INFO
	[string]$ScriptVersion = '2.6.4'
	[string]$ScriptTitle = "Install Windows Patches by Levon Becker"
	[int]$DashCount = '40'

	# CLEAR VARIABLES
	$Error.Clear()
	# (NOT IN TEMPLATE)
	[int]$Global:connectfailed = 0
	[int]$Global:vmfailed = 0
	[int]$TotalHosts = 0

	# LOCALHOST
	[string]$ScriptHost = $Env:ComputerNAME
	[string]$UserDomain = $Env:UserDomain
	[string]$UserName = $Env:UserName
	[string]$FileDateTime = Get-Date -UFormat "%Y-%m%-%d_%H.%M"
	[datetime]$ScriptStartTime = Get-Date
	$ScriptStartTimeF = Get-Date -Format g
		
	# DIRECTORY PATHS
	[string]$LogPath = ($Global:WindowsPatchingDefaults.IPLogPath)
	[string]$ScriptLogPath = Join-Path -Path $LogPath -ChildPath 'ScriptLogs'
	[string]$JobLogPath = Join-Path -Path $LogPath -ChildPath 'JobData'
	[string]$ResultsPath = ($Global:WindowsPatchingDefaults.IPResultsPath)
	
	[string]$ModuleRootPath = $Global:WindowsPatchingModulePath
	[string]$SubScripts = Join-Path -Path $ModuleRootPath -ChildPath 'SubScripts'
	[string]$Assets = Join-Path -Path $ModuleRootPath -ChildPath 'Assets'
	
	# CONVERT SWITCH TO BOOLEAN TO PASS AS ARGUMENT
	[boolean]$SkipDiskSpaceCheckBool = ($SkipDiskSpaceCheck.IsPresent)
	[boolean]$SkipVMHardwareBool = ($SkipVMHardware.IsPresent)
	[boolean]$SkipVMToolsBool = ($SkipVMTools.IsPresent)
	[boolean]$UseAltPCCredsBool = ($UseAltPCCreds.IsPresent)
	[boolean]$UseAltViCredsBool = ($UseAltViCreds.IsPresent)
	[boolean]$SkipAllVmwareBool = ($SkipAllVmware.IsPresent)
	
	#region  Set Logfile Name + Create HostList Array
	
		If ($ComputerName) {
			[string]$f = $ComputerName.ToUpper()
			# Inputitem used for WinTitle and Out-GridView Title at end
			[string]$InputItem = $ComputerName.ToUpper() #needed so the WinTitle will be uppercase
			[array]$HostList = $ComputerName.ToUpper()
		}
		ElseIf ($FileName) {
			[string]$f = $FileName
			[string]$HostListFileName = $FileName
			# Inputitem used for WinTitle and Out-GridView Title at end
			[string]$InputItem = $FileName
			If ($filebrowserused -eq $false) {
				[string]$HostListFullName = Join-Path -Path $HostListPath -ChildPath $FileName
			}
			If ((Test-Path -Path $HostListFullName) -ne $true) {
					Write-Host ''
					Write-Host "ERROR: INPUT FILE NOT FOUND ($HostListFullName)" -ForegroundColor White -BackgroundColor Red
					Write-Host ''
					Break
			}
			[array]$HostList = Get-Content $HostListFullName
			[array]$HostList = $HostList | ForEach-Object {$_.ToUpper()}
		}
		ElseIF ($List) {
			[array]$List = $List | ForEach-Object {$_.ToUpper()}
			[string]$f = "List - " + ($List | Select -First 2) + " ..."
			# Inputitem used for WinTitle and Out-GridView Title at end
			[string]$InputItem = "List: " + ($List | Select -First 2) + " ..."
			[array]$HostList = $List
		}
		Else {
			Write-Host ''
			Write-Host "ERROR: INPUT METHOD NOT FOUND" -ForegroundColor White -BackgroundColor Red
			Write-Host ''
			Break
		}
		[array]$HostList = $HostList | Select -Unique
		[int]$TotalHosts = $HostList.Count
	
	#endregion Set Logfile Name + Create HostList Array
	
	#region Determine TimeZone
	
		. "$SubScripts\Func_Get-TimeZone_1.0.0.ps1"
		Get-TimeZone -ComputerName 'Localhost'
		
		If (($Global:GetTimeZone.Success -eq $true) -and ($Global:GetTimeZone.ShortForm -ne '')) {
			[string]$TimeZone = "_" + $Global:GetTimeZone.ShortForm
		}
		Else {
			[string]$Timezone = ''
		}
	
	#endregion Determine TimeZone
	
	# FILENAMES
	[string]$ResultsTextFileName = "Install-Patches_Results_" + $FileDateTime + $Timezone + "_($f).log"
	[string]$ResultsCSVFileName = "Install-Patches_Results_" + $FileDateTime + $Timezone + "_($f).csv"
	[string]$JobLogFileName = "JobData_" + $FileDateTime + $Timezone + "_($f).log"

	# PATH + FILENAMES
	[string]$ResultsTextFullName = Join-Path -Path $ResultsPath -ChildPath $ResultsTextFileName
	[string]$ResultsCSVFullName = Join-Path -Path $ResultsPath -ChildPath $ResultsCSVFileName
	[string]$JobLogFullName = Join-Path -Path $JobLogPath -ChildPath $JobLogFileName


#endregion Variables

#region Check Dependencies
	
	[int]$depmissing = 0
	$depmissingList = $null
	# Create Array of Paths to Dependancies to check
	CLEAR
	$depList = @(
		"$SubScripts",
		"$SubScripts\Func_Add-HostToLogFile_1.0.4.ps1",
		"$SubScripts\Func_Connect-ViHost_1.0.7.ps1",
		"$SubScripts\Func_Disconnect-VIHost_1.0.1.ps1",
		"$SubScripts\Func_Get-DiskSpace_1.0.1.ps1",
		"$SubScripts\Func_Get-JobCount_1.0.3.ps1",
		"$SubScripts\Func_Get-HostIP_1.0.5.ps1",
		"$SubScripts\Func_Get-IPConfig_1.0.4.ps1",
		"$SubScripts\Func_Get-OSVersion_1.0.9.ps1",
		"$SubScripts\Func_Get-PendingReboot_1.0.6.ps1",
		"$SubScripts\Func_Get-RegValue_1.0.5.ps1",
		"$SubScripts\Func_Get-Runtime_1.0.3.ps1",
		"$SubScripts\Func_Get-TimeZone_1.0.0.ps1",
		"$SubScripts\Func_Get-VmGuestInfo_1.0.5.ps1",
		"$SubScripts\Func_Get-VmHardware_1.0.5.ps1",
		"$SubScripts\Func_Get-VmTools_1.0.9.ps1",
		"$SubScripts\Func_Invoke-Patching_1.4.8.ps1",
		"$SubScripts\Func_Invoke-PSExec_1.0.7.ps1",
		"$SubScripts\Func_Reset-WPMUI_1.0.2.ps1",
		"$SubScripts\Func_Restart-Host_1.0.8.ps1",
		"$SubScripts\Func_Remove-Jobs_1.0.5.ps1",
		"$SubScripts\Func_Show-ScriptHeader_1.0.2.ps1"
		"$SubScripts\Func_Send-VMPowerOff_1.0.4.ps1",
		"$SubScripts\Func_Send-VMPowerOn_1.0.4.ps1",
		"$SubScripts\Func_Test-Connections_1.0.6.ps1",
		"$SubScripts\Func_Test-Permissions_1.1.0.ps1",
		"$SubScripts\Func_Update-VmHardware_1.0.6.ps1",
		"$SubScripts\Func_Update-VmTools_1.0.8.ps1",
		"$SubScripts\Func_Watch-Jobs_1.0.3.ps1",
		"$SubScripts\MultiFunc_StopWatch_1.0.1.ps1",
		"$SubScripts\MultiFunc_Set-WinTitle_1.0.5.ps1",
		"$SubScripts\MultiFunc_Out-ScriptLog_1.0.3.ps1",
		"$SubScripts\MultiFunc_Show-Script-Status_1.0.3.ps1",
		"$SubScripts\Install-Patches_1.0.3.vbs",
		"$LogPath",
		"$LogPath\History",
		"$LogPath\JobData",
		"$LogPath\Latest",
		"$LogPath\Temp",
		"$LogPath\WIP",
		"$HostListPath",
		"$ResultsPath",
		"$SubScripts",
		"$Assets"
	)

	Foreach ($deps in $depList) {
		[boolean]$checkpath = $false
		$checkpath = Test-Path -Path $deps -ErrorAction SilentlyContinue 
		If ($checkpath -eq $false) {
			$depmissingList += @($deps)
			$depmissing++
		}
	}
	If ($depmissing -gt 0) {
		Write-Host "ERROR: Missing $depmissing Dependancies" -ForegroundColor White -BackgroundColor Red
		$depmissingList
		Write-Host ''
		Break
	}

#endregion Check Dependencies

#region Functions

	. "$SubScripts\Func_Add-HostToLogFile_1.0.4.ps1"
	. "$SubScripts\Func_Get-JobCount_1.0.3.ps1"
	. "$SubScripts\Func_Get-Runtime_1.0.3.ps1"
	. "$SubScripts\Func_Invoke-Patching_1.4.8.ps1"
	. "$SubScripts\Func_Remove-Jobs_1.0.5.ps1"
	. "$SubScripts\Func_Reset-WPMUI_1.0.2.ps1"
	. "$SubScripts\Func_Show-ScriptHeader_1.0.2.ps1"
	. "$SubScripts\Func_Test-Connections_1.0.6.ps1"
	. "$SubScripts\MultiFunc_StopWatch_1.0.1.ps1"
	. "$SubScripts\MultiFunc_Out-ScriptLog_1.0.3.ps1"
	. "$SubScripts\MultiFunc_Set-WinTitle_1.0.5.ps1"
	. "$SubScripts\MultiFunc_Show-Script-Status_1.0.3.ps1"

#endregion Functions

#region Set Window Title
	
	Set-WinTitle-Start -title $ScriptTitle
	Show-ScriptHeader -blanklines '1' -DashCount $DashCount -ScriptTitle $ScriptTitle

#endregion Set Window Title

#region Check Vmware Prerequisites

	If (($SkipVMTools.IsPresent -eq $false) -or ($SkipAllVmware.IsPresent -eq $false)) {

	#region Get PowerCLI Version
	
		#Get OS ARCH
		If (((Get-WmiObject win32_operatingSystem -ComputerName localhost).OSArchitecture) -eq '64-bit') {
			$ScriptHostArch = '64'
		}
		Else {
		$ScriptHostArch = '32'
		}
		If ($ScriptHostArch -eq '64') {
			$vmwareregpath = 'hklm:\SOFTWARE\Wow6432Node\VMware, Inc.'
		}
		Else {
			$vmwareregpath = 'hklm:\SOFTWARE\VMware, Inc.'
		}

		$pcliregpath = $vmwareregpath + '\VMware vSphere PowerCLI'
		If ((Test-Path -Path $pcliregpath) -eq $true) {
			$pcliver = (Get-ItemProperty -Path $pcliregpath -name InstalledVersion).InstalledVersion
			If (($pcliver -match '4.') -or ($pcliver -match '5.')) {
			}
			Else {
				Write-Host ''
				Write-Host "ERROR: INCORRECT VERSION OF VMWARE POWERCLI ($pcliver)" -ForegroundColor White -BackgroundColor Red
				Write-Host 'TIP: Vmware PowerCLI Must Be Version 4.1 or Higher'
				Write-Host ''
				Break
			}
		}
		Else {
			Write-Host ''
			Write-Host "ERROR: VMWARE POWERCLI IS NOT INSTALL ON $ScriptHost" -ForegroundColor White -BackgroundColor Red
			Write-Host ''
			Break
		}

	#endregion Check PowerCLI Version

	#region Ping vCenter

		If ($vCenter) {
			If ((Test-Connection -ComputerName $vCenter -Count 2 -Quiet) -eq $true) {
			}
			Else {
				Write-Host ''
				Write-Host "ERROR: CAN NOT PING VCENTER ($vCenter)" -ForegroundColor White -BackgroundColor Red
				Write-Host ''
				Break
			}
		}

	#endregion Ping vCenter
	
	}

#endregion Check Vmware Prerequisites

#region Console Start Statements

	Show-ScriptHeader -blanklines '4' -DashCount $DashCount -ScriptTitle $ScriptTitle
	# Get PowerShell Version with External Script
	If (($SkipVMTools.IsPresent -eq $true) -or ($SkipAllVmware.IsPresent -eq $true)) {
		Set-WinTitle-Base -ScriptVersion $ScriptVersion 
	}
	Else {
		Set-WinTitle-Base -ScriptVersion $ScriptVersion -IncludePowerCLI
	}
	
	[datetime]$ScriptStartTime = Get-Date
	[string]$ScriptStartTimeF = Get-Date -Format g
#	Out-ScriptLog-Starttime -StartTime $ScriptStartTimeF -ScriptLogFullName $ScriptLogFullName

#endregion Console Start Statements

#region Add ScriptLog Header

#	Out-ScriptLog-Header -ScriptLogFullName $ScriptLogFullName -ScriptHost $ScriptHost -UserDomain $UserDomain -UserName $UserName

#endregion Add ScriptLog Header

#region Test and Patch

	Set-WinTitle-Input -wintitle_base $Global:wintitle_base -InputItem $InputItem

	#region Test Connections
		
		Test-Connections -List $HostList -MaxJobs '25' -TestTimeout '120' -JobmonTimeout '600' -SubScripts $SubScripts -ResultsTextFullName $ResultsTextFullName -JobLogFullName $JobLogFullName -TotalHosts $TotalHosts -DashCount $DashCount -ScriptTitle $ScriptTitle -UseAltPCCredsBool $UseAltPCCredsBool -PCCreds $PCCreds -WinTitle_Input $Global:WinTitle_Input
		
		If ($Global:TestConnections.Success -eq $true) {
			[array]$HostList = $Global:TestConnections.PassedList
		}
		[int]$TotalHosts = $Global:TestConnections.PassedCount
		Show-ScriptHeader -blanklines '1' -DashCount $DashCount -ScriptTitle $ScriptTitle

	#endregion Test Connections
	
	#region Run Patching 
		
		# Create Results Text file with header
		Add-Content -Path $ResultsTextFullName -Encoding ASCII -Value 'Hostname,Complete Success,Patching Success,DiskSpace OK,VmT OK,VmH OK,Patches,Reboots,Connected,VmT Upgraded,VmH Upgraded,Runtime,Starttime,Endtime,OSVersion,OSArch,HostIP,C: Size (MB),C: Free (MB),Reboot Times,VmTools Version,VmHardware Version,VmHost,VmHost Version,Datastores,WUVBS Exitcode,Errors,Script Version,Script Host,User'
				
		#Add Failed Connection Systems to Results Text File
		If ($Global:TestConnections.FailedCount -gt '0') {
			Get-Runtime -StartTime $ScriptStartTime
			[string]$FailedConnectResults = 'False,False,Unknown,Unknown,Unknown,0,0,False' + ',' + $Global:GetRuntime.Runtime + ',' + $ScriptStartTimeF + ',' + $Global:GetRuntime.EndTimeF + ',' + "Unknown,Unknown,Unknown,Unknown,Unknown,N/A,N/A,Unknown,Unknown,N/A,Unknown,Unknown,N/A,Failed Connection" + ',' + $ScriptVersion + ',' + $ScriptHost + ',' + $UserName
			$Global:TestConnections.FailedList | Foreach-Object {Add-Content -Path $ResultsTextFullName -Encoding ASCII -Value ($_ + ',' + $FailedConnectResults)}
		}
		
		If (($SkipVMTools.IsPresent -eq $true) -or ($SkipAllVmware.IsPresent -eq $true)) {
		
			Invoke-Patching -HostList $HostList -SubScripts $SubScripts -Assets $Assets -ScriptVersion $ScriptVersion -JobLogFullName $JobLogFullName -MinFreeMB $MinFreeMB -UserDomain $UserDomain -UserName $UserName -ScriptHost $ScriptHost -FileDateTime $FileDateTime -LogPath $LogPath -MaxJobs $MaxJobs -ResultsTextFullName $ResultsTextFullName -UseAltViCredsBool $UseAltViCredsBool -UseAltPCCredsBool $UseAltPCCredsBool -PCCreds $PCCreds -TotalHosts $TotalHosts -DashCount $DashCount -ScriptTitle $ScriptTitle -SkipDiskSpaceCheckBool $SkipDiskSpaceCheckBool -SkipVMToolsBool $SkipVMToolsBool -SkipVMHardwareBool $SkipVMHardwareBool -SkipAllVmwareBool $SkipAllVmwareBool -JobQueTimeout $JobQueTimeout
		}
		Else {
			Invoke-Patching -HostList $HostList -SubScripts $SubScripts -Assets $Assets -ScriptVersion $ScriptVersion -JobLogFullName $JobLogFullName -vCenter $vCenter -MinFreeMB $MinFreeMB -UserDomain $UserDomain -UserName $UserName -ScriptHost $ScriptHost -FileDateTime $FileDateTime -LogPath $LogPath -MaxJobs $MaxJobs -ResultsTextFullName $ResultsTextFullName -UseAltViCredsBool $UseAltViCredsBool -ViCreds $ViCreds -UseAltPCCredsBool $UseAltPCCredsBool -PCCreds $PCCreds -TotalHosts $TotalHosts -DashCount $DashCount -ScriptTitle $ScriptTitle -SkipDiskSpaceCheckBool $SkipDiskSpaceCheckBool -SkipVMToolsBool $SkipVMToolsBool -SkipVMHardwareBool $SkipVMHardwareBool -SkipAllVmwareBool $SkipAllVmwareBool -JobQueTimeout $JobQueTimeout
		}

	#endregion Run Patching

#endregion Test and Patch

#region Convert Results Text File to CSV
	
	# Import text file as CSV formated variable - Used for outgrid and CSV file creation
	$outfile = Import-Csv -Delimiter ',' -Path $ResultsTextFullName
	# Create CSV file with CSV formated variable
	$outfile | Export-Csv -Path $ResultsCSVFullName -NoTypeInformation
	# Delete text file if CSV file was created successfully
	If ((Test-Path -Path $ResultsCSVFullName) -eq $true) {
		Remove-Item -Path $ResultsTextFullName -Force
	}

#endregion Convert Results Text File to CSV

#region Script Completion Updates

	Show-ScriptHeader -blanklines '1' -DashCount $DashCount -ScriptTitle $ScriptTitle
	Get-Runtime -StartTime $ScriptStartTime
	Show-ScriptStatus-RuntimeTotals -StartTimeF $ScriptStartTimeF -EndTimef $Global:GetRuntime.Endtimef -Runtime $Global:GetRuntime.Runtime
	[int]$TotalHosts = $Global:TestPermissions.PassedCount
	Show-ScriptStatus-TotalHosts -TotalHosts $TotalHosts
	Show-ScriptStatus-Files -ResultsPath $ResultsPath -ResultsFileName $ResultsCSVFileName -LogPath $LogPath
	
	# WRITE ERRORS TO SCRIPTLOG
#	If ($Error) {
#		Out-Script-Errors -ScriptLogFullName $ScriptLogFullName -Errors $Error
#	}
	
	If ($Global:WatchJobs.JobTimeOut -eq $true) {
#		Out-ScriptLog-JobTimeout -ScriptLogFullName $ScriptLogFullName -JobmonNotes $Global:WatchJobs.Notes -EndTime $Global:GetRuntime.Endtime -Runtime $Global:GetRuntime.Runtime
		Show-ScriptStatus-JobLoopTimeout
		Set-WinTitle-JobTimeout -WinTitle_Input $Global:WinTitle_Input
		
		# Cleanup WIP Files
		Foreach ($ComputerName in $HostList) {
			[string]$WIPPath = Join-Path -Path $LogPath -ChildPath 'WIP' 
			[string]$WIPFullName = Join-Path -Path $WIPPath -ChildPath $ComputerName
			If ((Test-Path -Path $WIPFullName) -eq $true) {
				Remove-Item -Path $WIPFullName -Force
			}
		}
	}
	Else {
#		Out-ScriptLog-Footer -EndTime $Global:GetRuntime.Endtime -Runtime $Global:GetRuntime.Runtime -ScriptLogFullName $ScriptLogFullName
		Show-ScriptStatus-Completed
		Set-WinTitle-Completed -WinTitle_Input $Global:WinTitle_Input
	}

#endregion Script Completion Updates

#region Display Report

	If ($SkipOutGrid.IsPresent -eq $false) {
		$outfile | Out-GridView -Title "Windows Patching Results for $InputItem"
	}

#endregion Display Report

#region Cleanup UI

	Reset-WPMUI -StartingWindowTitle $StartingWindowTitle -StartupVariables $StartupVariables -SubScripts $SubScripts

#endregion Cleanup UI

} #Function

#region Notes

<# Dependants
	None
#>

<# Dependencies
Func_Get-Runtime
Func_Remove-Jobs
Func_Get-JobCount
Func_Watch-Jobs
MultiFunc_StopWatch
MultiFunc_Set-WinTitle
MultiFunc_Out-ScriptLog
MultiFunc_Show-Script-Status
Func_Add-HostToLogFile
Func_Get-PendingReboot
Func_Get-DiskSpace
Func_Get-VmHardware
Func_Get-VmTools
Func_Get-HostIP
Func_Get-IPconfig
Func_Get-OSVersion
Func_Get-RegValue
Func_Get-VmGuestInfo
Func_Restart-Host
Func_Invoke-PSExec
Func_Test-Connections
Func_Reset-WPMUI
Func_Send-VMPowerOff
Func_Send-VMPowerOn
Func_Update-VmHardware
Func_Update-VmTools
Func_ConvertTo-ASCII
Func_Connect-VIHost
Func_Disconnect-VIHost
Func_Invoke-Patching
Multi_Write-Logs
Install-Patches.vbs
#>

<#	Sources
#Starter Code
	http://social.technet.microsoft.com/Forums/en-US/ITCG/thread/ec3cb986-41af-43e5-a119-80dcb2567f4e 
#Powershell Syntax
	http://www.Computerperformance.co.uk/powershell/powershell_syntax.htm
#Advanced Functions
	http://technet.microsoft.com/en-us/library/dd315326.aspx
#Function Parameter Example
	http://poshcode.org/2058
#Using Functions like Goto statement
	http://www.powergui.org/thread.jspa?threadID=10043
#Write-Host
	http://technet.microsoft.com/en-us/library/ee177031.aspx
#Deleting Entire Lines in Text Files
	http://superuser.com/questions/117650/deleting-entire-lines-in-a-text-file-based-on-a-partial-string-match-with-windows
#Variables and Variable Values
	http://technet.microsoft.com/en-us/library/ee692790.aspx
#Pending Reboot Check Example
	http://www.techmumbojumblog.com/?p=375
#Try/Catch
	http://outputredirection.blogspot.com/2010/04/powershells-trycatchfinally-and.html
#Auto-Help Header Syntax
	http://pshscripts.blogspot.com/2008/12/get-autohelpps1.html
#Vmware Vmtools Upgrade
	http://get-admin.com/blog/?p=714&cpage=1#comment-1319
	http://blogs.techrepublic.com.com/datacenter/?p=3502
	http://get-admin.com/blog/?p=714 
#Copy-Item
	http://ashrafur.wordpress.com/2009/12/14/powershell-copy-file-and-folder-to-remote-location/
#Remote Registry PowerShell Module
	http://code.msdn.microsoft.com/PSRemoteRegistry/Release/ProjectReleases.aspx?ReleaseId=3781
#Add Vmware PowerCLI Snapin	
	http://vmwire.blogspot.com/2009/05/power-cli-quick-start-guide.html
#Drive Space Check Example
	http://social.technet.microsoft.com/Forums/en-US/winserverpowershell/thread/1559645d-2f24-49c2-9d3a-7bbbd225691c/
#Vmware PowerCLI 4.1.1 Crashes in 64-bit PowerShell Job
	http://communities.vmware.com/thread/280979?start=0&tstart=0
#WMI Error if Shortname used
	http://social.msdn.microsoft.com/Forums/en/netfxremoting/thread/10b9391b-19c4-4c0c-b31e-45b91831de00
#Match/Like/Contains Operators
	http://www.Computerperformance.co.uk/powershell/powershell_conditional_operators.htm
#Test TCP Port
	http://powergui.org/message.jspa?messageID=32729#32729
	http://halr9000.com/article/418
#Query OS Architecture Example
	http://social.technet.microsoft.com/Forums/en/winserverpowershell/thread/7e26154c-c737-4158-acd5-d4a659902684
#Get Local Registry Value
	http://social.technet.microsoft.com/Forums/en/ITCG/thread/1208d07c-0a54-4c80-b888-3a0cadce7ff5
#Remove Value from Array
	http://www.powershellcommunity.org/Forums/tabid/54/aff/1/aft/3993/afv/topic/Default.aspx
#Connect to Remote SQL Database
	http://www.ComputerNamecentercentral.com/BlogDetails/tabid/143/indexid/60012/Default.aspx
	http://msdn.microsoft.com/en-us/library/cc281962.aspx
#Custom GUI Input Box
	http://technet.microsoft.com/en-us/library/ff730941.aspx
#Get-WMIObject for Network Configuration
	http://lantoolbox.com/articles/performing-networking-tasks-viewing-network-settings-from-powershell/
#Escape Characters "Backtick"
	http://www.Computerperformance.co.uk/powershell/powershell_backtick.htm
#StopWatch
	http://msdn.microsoft.com/en-us/library/ComputerName.diagnostics.stopwatch.stop(VS.90).aspx
#Elapsed Time
	http://poshtips.com/2010/03/30/measuring-elapsed-time-in-powershell/
#Query 32/64 bit PowerShell Session Running Example
	http://msgoodies.blogspot.com/2008/05/is-this-powershell-session-32-bit-or-64.html
#Regsrv32.exe
	http://support.microsoft.com/default.aspx?scid=KB;EN-US;q249873&
#Random Number Object Example
	http://tfl09.blogspot.com/2007/01/random-numbers-in-powershell.html
#Advanced Debugging
	http://blogs.msdn.com/b/powershell/archive/2009/07/13/advanced-debugging-in-powershell.aspx
#PSExec Error with Jobs
	http://forum.sysinternals.com/psexec-hangs-when-run-in-powershell-startjob_topic23352.html
#Script Logger Module
	http://www.nivot.org/2009/08/19/PowerShell20AConfigurableAndFlexibleScriptLoggerModule.aspx
#Understanding Output
	http://keithhill.spaces.live.com/Blog/cns!5A8D2641E0963A97!811.entry
#Try/Catch/Finally Commands
	http://blogs.technet.com/b/heyscriptingguy/archive/2010/03/11/hey-scripting-guy-march-11-2010.aspx
#Redirects / Pipes
	http://www.techotopia.com/index.php/Windows_PowerShell_1.0_Pipes_and_Redirection
#Working with STDERR "Standard Error"
	http://halr9000.com/article/837
#Trace-Command
	http://www.activexperts.com/activmonitor/windowsmanagement/powershell10/0144/
#Using .NET 4.0 to access x64 remote registry
	http://www.poshpete.com/powershell/accessing-remote-x64-registry-from-an-x86x32-server
#Enabling .NET 4.0 in PowerShell 2.0
	http://www.poshpete.com/powershell/enabling-net-4-in-your-powershell-environment
	http://stackoverflow.com/questions/2094694/how-can-i-run-powershell-with-the-net-4-Runtime
	http://stackoverflow.com/questions/5475122/how-to-use-add-type-with-path-and-also-language-csharpversion3
#Alternate Credintials
	http://technet.microsoft.com/en-us/library/dd315327.aspx
#>

<# To Do List
 	1. Add browse for file feature?
 #>

#region Change Log

<# Version 2.x

2.0.0 - 04/08/2011 (Alpha)
	Moved directory location to C:\Scripts\Windows-Patching. Pulled data sections and pasted 
	into my jobloop script template. 
2.0.0 - 04/11/2011
	Continued cleanup and started testing in new template format.  
	Got most of the basic bugs worked out.
2.0.0 - 04/12/2011
	Continued reformatting and splitting code off to external files.
	Started logic build for summary CSV Results file.
2.0.1 - 04/13/2011
	Moved and cleaned up Prompt for Missing Host Input to top because the varible consolidation
	broke the setting of the path variables for the host inputs.
2.0.1 - 04/20/2011
	Fixed StartTimeF variable to not call globally
2.0.2 - 04/22/2011
	Cleaned up information sections and changed to new format.
2.0.3 - 04/27/2011
	Added Method ToUpper on Hosts to make them always uppercase.
2.0.6 - 05/05/2011
	Changed PSVersion to new PSObject global variable.
	Added conversion of Results text file to csv.
	Changed Get-DiskSpace $minfree to $MinFreeMB
2.0.7 - 05/05/2011
	Added Out-GridView at end
2.0.8 - 05/08/2011
	Changed Results to include more diskcheck info.
2.0.9 - 05/13/2011
	Removed extra vCenter var setting.
2.1.0 - 05/13/2011
	Fixed History Log Append from Latest Log in Run-Patching
	Added Adminhost and UserName to Results file
2.1.1 - 07/18/2011
	Moved MaxJobs to Parameter.
	Increased the max jobs default from 120 to 200
2.1.2 - 09/14/2011
	Fixed it so it will allow Vmware PowerCLI version 5.x
2.1.3 - 10/02/2011
	Added Vmtools and VmHardware version to Results
2.1.4 - 10/05/2011
	Added Script Version, Host IP and VMDatastores header to Results file
2.1.5 - 10/09/2011
	Troubleshooting Latest Log missing patches
	Combined Host Method and Log File Name to one section
	Added Dependancies path check at start
	Removed subfolders for input Lists
2.1.6 - 10/09/2011
	Removed Ordered FileList and FileList
2.1.7 - 10/09/2011
	Added $Global:HostList so it only runs the trimmed down List not including ComputerNames that failed the Test-Permissions Function
2.1.8 - 10/09/2011
	Moved the Test Permissions and Run Patching to single lines following the hostmethod variable setting
2.1.9 - 10/11/2011
	Fixed up Total Hosts
	Fixed up Test Permissions host console writes
	Added Dependants Get-HostIP and Get-VmGuestInfo
2.2.0 - 10/13/2011
	Removed Second Reboot failure in Results file header
	Rearranged Results header columns
	Added Clientlatest log empty and found to Results header
2.2.1 - 10/13/2011
	Switched Update.vbs to SearchDownloadInstall-WUA.vbs dependency check
2.2.2 - 11/04/2011
	Cleaned up some code for InputItem at end to remove redundant code.
	Indented regions for easier viewing.
2.2.3 - 11/04/2011
	Added specific variable types
	Consolidated InputItem, filename, hostmethod and HostList creation up top in Variables
	Changed Hostfile to FileName
	Removed hostmethod
	Added ArrayList Parameter and logic to feed an array or List of hostnames to the cmdlet without a file
	Split the Change log up so I can Min the 1.x stuff
	Fixed some upper case stuff in the HostList creation and InputItem set
2.2.4 - 11/04/2011
	Added missing $List $f filename variable
2.2.5 - 11/07/2011
	Fixed Missing input prompt to work with new List parameter
	Increase default jobmax to 320
2.2.6 - 11/07/2011
	Changed to use 64-bit PowerShell so Pending Reboot .NET object can check both 32 and 64 registries.
2.2.7 - 11/10/2011
	Changed back to 32-bit PowerShell check
	Changed to Run-patching 1.2.0
	Now using .NET 4.0 in PowerShell to solve the Get-PendingReboot issue with looking up 64-bit registrys
	Added back VICREDS option
2.2.8 - 11/10/2011
	Working out credintials issue
	Removed VIC parameter (not needed)
	Changed Sub Scripts to new version naming
2.2.9 - 11/11/2011
	Changed to all the sub scripts that use Func_Connect-VIHost_1.0.3 to newer version
	Including Run-Patching to 1.2.1
2.3.0 - 11/21/2011
	Cleaned up some code
	Changed to Run-Patching_1.2.2
2.3.1 - 01/31/2012
	Changed to Run-Patching_1.2.3
2.3.2 - 01/31/2012
	Changed Run-Patching to 1.2.4
	Updated Remove-Jobs to 1.0.3
	Updated Watch-Jobs to 1.0.1
	Updated Get-JobCount to 1.0.2
	Added Test-Connection SubScript and replace Test-Permissions section
	Removed Test-Permissions SubScript
2.3.2 - 02/03/2012
	Removed completesuccesslog
2.3.3 - 02/03/2012
	Changed Run-Patching to 1.2.5
	Added Failed_Access Path and Filename
	Update MultiFunc_Out-ScriptLog_1.0.2
	Added all missing parameters for Out-ScriptLog-Header
2.3.3 - 02/07/2012
	Added spacing at end of Show-ScriptHeader for new Progress bars
	Finished removing all the extra failed logs
	Added $psversion + rename $psver to $psversion
	Added $CLRVersion
	Updated to Multi_Wintitle-Display_1.0.3
	Updated to Func_Watch-Jobs_1.0.1
2.3.4 - 02/07/2012
	Added blank line logic and parameter to change the spacing for when the progress bar is visable
	to Show-ScriptHeader local function.
	Changed Set-WinTitle-Notice to Set-WinTitle-Start
2.3.5 - 02/07/2012
	Changed Script Completion Updates section at end so that it will finish with the Results and
	complete the script. Some or most may have been ok and it was just hung on a few.
2.3.6 - 02/08/2012
	Still tracking down the error at the end in the Job Cleanup
	Found it! It was the Receive-Job (data) that had "errors" in the Results from
		the psexec Results that would stop the script with $ErrorActionPreference = "Inquire" set
		because even though they were "ok" errors (returned error 0) it still saw it as an error
		and would flag. So I added -ErrorAction ContinueSilently to the Receive-Job action
		in Func_Job-Cleanup_1.0.3 subscript.
	Removed PSExec.exe path variables
	Replace PSExec lines with newly built subscript Invoke-PSExec_1.0.1
	Updated to Func_Run-Patching_1.2.6 that has PSExec method changed
2.3.7 - 02/09/2012
	Changed WUScript Success to WUAVBS Success header title on Results file.
2.3.8 - 02/09/2012
	Updated to Func_Run-Patching_1.2.7 batch file to run VB Script
2.3.9 - 02/09/2012
	Updated to Func_Run-Patching_1.2.8 Cleanup UI Results
2.4.0 - 02/10/2012
	Updated to Func_Run-Patching_1.2.9
2.4.1 - 02/10/2012
	Updated to Func_Run-Patching_1.3.0
2.4.2 - 02/10/2012
	Changed $outfile for CSV back to pipe into $outfile | Export-Csv -Path $ResultsCSVFullName -NoTypeInformation
2.4.3 - 02/10/2012
	Converted Show-ScriptHeader Local Function to subscript Function
	Updated to Func_Run-Patching_1.3.1 (Using net Show-ScriptHeader Subscript)
2.4.4 - 04/16/2012
	Renamed several variables
	Change _Shared-Dependencies to _SubScripts
	Cleaned up Parameter case.
	Change $hdc variable to $GetDiskSpace switch.
	Changed log formatting to better layout.
	Moved CheckFreeSpace prompt to under if missing host input
	Switched to Func_Run-Patching_1.3.2
	Switched to Func_Add-Header_1.0.1
	Added $DashCount to parent script and pass to Run-Patching subscript
	Added GetVmHardware and GetVmTools switch parameters. need logic
2.4.5 - 04/16/2012
	Changed CheckFreeSpace to SkipDiskSpaceCheck
	Changed GetVmHardware and GetVmTools to Skip
	Added UseAltViCreds switch Parameter
2.4.6 - 04/19/2012
	SubScript renames
	Removed Multi_Check-JobLoopScript-Parameters subscript
	Removed Func_Get-PSVersion subscript
	Switched to Func_Run-Patching_1.3.5
	Switched to Func_Test-Connections_1.0.1
		Added Show-ScriptHeader parameters
2.4.7 - 04/20/2012
	Switch to Func_Run-Patching_1.3.7
	Renamed parameter FileName to FileName
2.4.9 - 04/20/2012
	Switch to Func_Run-Patching_1.3.8
	More renaming parameters and tweaking new log method
2.5.0 - 04/23/2012
	Moved Results columns around and renamed some
	Switched to Func_Run-Patching_1.3.9
2.5.1 - 04/24/2012
	Changed Title. Noticed having the script version was redundant since I added it to the Console title.
2.5.2 - 04/25/2012
	Turned parent script into function so can be used in module
	Added position 0 to ComputerName
	Added Cleanup section at end and start
2.5.3 - 04/26/2012
	Renamed a ton more through all scripts to conform with approved cmdlet verbs. Working towards
	all sub scripts to be contained in WindowsPatching Module.
	Added switch to skip Out-Gridview
2.5.4 - 04/27/2012
	Changed Cleanup Section to Reset-WPMUI subscript.
2.5.6 - 04/30/2012
	Host Method Prompt cleanup and changes.
	Added several Do Until loops in prompts.
	Added Get-FileNames File Browser for FileName.
	Fixed Alt Creds prompt (Condition was backwards).
	Removed vcenter parameter default.
	Added vCenter FQDN prompt.
	Added FileBrowse Switch and draft logic.
	Changed name of altvicreds to usealtvicreds
	Changed name of usealtvicreds to usealtvicredsbool
	Added UseAltPCCreds parameter
	Added UseAltPCCredsBool variable
	Added PCCreds variable and draft logic
	Changed AltViCreds to ViCreds variable
2.5.8 - 05/03/2012
	Changed folder locations and some names using Set-WindowsPatchingDefault Global variable.
	Changed Windows-Patching to WindowsPatching remote folder
	Added remove / copy logs of Windows-Patching folder on remote system
	Renamed a lot of variables so they make more since and continued improvement on my own stardards
	Fixed some rename mistakes such as ConnectToViHost to ConnectViHost
	Changed Set-Header to Show-ScriptHeader
	Added Error handling at start for if Set-WindowsPatchingDefaults wasn't ran
	Fixed Out-ScriptLog-Error
	Added Out-ScriptLog-Errors specifically for writing $Error to log, may not need it
	Added Show-ScriptStatus-Files function to give path to output files and folders
2.5.9 - 05/07/2012
	Renamed Get-FileName to Func_Get-FileName and moved to SubScripts
	Changed the module to not load Get-FileName and have the scripts call it if needed.
2.6.0 - 05/08/2012
	Added JobQueTimeout parameter
	Switch to Invoke-patching 1.4.6 that now includes the JobQueTimeout parameter as well
	Tweaked the Maxjobs and JobQueTimeouts to be more efficient
	Added WIP file cleanup for it Watch-Jobs Timeout.
	Switched to Test-Connections 1.0.4
	Switched to Watch-Jobs 1.0.3
	Added Get-TimeZone to pull localhost timezone for file names.
2.6.1 - 05/10/2012
		Renamed Reset-UI to Reset-WPMUI
2.6.2 - 05/11/2012
	Fixed Prompt for AltViCreds. It needed to be outside Missing Host Vmware Prompt Group.
2.6.3 - 05/14/2012
	Switched to Show-WPMTip 1.0.2
2.6.3 - 05/15/2012
	Added Hosts that failed connection test to Results.
	Switched to Invoke-Patching 1.4.7
2.6.4 - 05/16/2012
	Switched to Get-OSVersion 1.0.9
	Switched to Invoke-Patching 1.4.8
	Removed FailedAccess logic now that it's in the results.
	Switched to Test-Connections 1.0.6
#>

#endregion Change Log

#endregion Notes
